#include "osd_text.h"
#include "common/common_tool.h"

namespace stream {

HBOsdText::HBOsdText(HBStreamOsdInfo* osd_info, LogInfo* log_info):
  BaseOsd(osd_info, log_info) {

    chn.s32PipelineId = osd_info->VpsGrp;
    // 画图时的通道必须是VPS通道号+1
    chn.enChnId = osd_info->vpsChn+1;
}

HBOsdText::~HBOsdText() {

}

int HBOsdText::release_params() {
  release_draw_params();
  return 0;
}


int HBOsdText::init_params() {
  int ret = 0;
  HBStreamOsdInfo* osd_info = (HBStreamOsdInfo *)osd_ifo;
  
  VPS_GRP_ATTR_S grp_attr;
  memset(&grp_attr, 0, sizeof(VPS_GRP_ATTR_S));
  grp_attr.maxW = osd_info->dstWidth;
  grp_attr.maxH = osd_info->dstHeight;
  grp_attr.frameDepth = 8;
  ret = HB_VPS_CreateGrp(osd_info->VpsGrp, &grp_attr);
  if (ret != 0) {printf("HB_VPS_CreateGrp: %d\n", ret); return ret;}
    
  ret = HB_SYS_SetVINVPSMode(osd_info->VpsGrp, VIN_OFFLINE_VPS_OFFINE);
  if (ret != 0) {printf("HB_SYS_SetVINVPSMode: %d\n", ret); return ret;}
  
  VPS_CHN_ATTR_S chn_attr;
  memset(&chn_attr, 0, sizeof(VPS_CHN_ATTR_S));
  chn_attr.enScale = 1;
  chn_attr.width = osd_info->dstWidth;
  chn_attr.height = osd_info->dstHeight;
  chn_attr.frameDepth = 8;
  HB_VPS_SetChnAttr(osd_info->VpsGrp, osd_info->vpsChn, &chn_attr);
  HB_VPS_EnableChn(osd_info->VpsGrp, osd_info->vpsChn);
  HB_VPS_StartGrp(osd_info->VpsGrp);


  // 初始化数据区
  memset(&feedback_buf, 0, sizeof(hb_vio_buffer_t));
  int size_y = osd_info->dstWidth * osd_info->dstHeight;
  int size_uv = size_y / 2;
  ret = prepare_user_buf(&feedback_buf, size_y, size_uv);
  if (ret != 0) { printf("error. prepare_user_buf: %d\n", ret); return ret; }

  feedback_buf.img_info.planeCount = 2;
  feedback_buf.img_info.img_format = 8;
  feedback_buf.img_addr.width = osd_info->dstWidth;
  feedback_buf.img_addr.height = osd_info->dstHeight;
  feedback_buf.img_addr.stride_size = osd_info->dstWidth;

  ret = set_draw_params();
  printf("set_draw_params VpsGrp:%d, vpsChn:%d, ret:%d\n", osd_info->VpsGrp, osd_info->vpsChn, ret);
  if (ret != 0) { release_draw_params(); }
  return ret;
}

int HBOsdText::draw_osd(stream::ImageBlob& buffer, std::vector<OSDDrawInfo>& osdd_ifos) {
  int ret = 0;
  HBStreamOsdInfo* osd_info = (HBStreamOsdInfo *)osd_ifo;
  // mtx.lock();

  size_t size_y = osd_info->dstWidth * osd_info->dstHeight;
  size_t size_uv = size_y / 2;
  memset(bitmapAttr.pAddr, 0xff, size_uv);

  RGN_DRAW_LINE_S drawLine[4];
  RGN_DRAW_WORD_S drawWord;
  int error = 0;
  for (auto& osdd_ifo : osdd_ifos) {
    int box_color = osd_info->boxColorOK;
    for (size_t idx = 0; idx < osdd_ifo.texts.size(); idx++) {
      error = 0;
      // drawWord.bInvertEn = 1;
      drawWord.enFontSize = osd_info->fontSize;
      // drawWord.enFontColor = (RGN_FONT_COLOR_E)osd_info->textColor;
      drawWord.enFontColor = (RGN_FONT_COLOR_E)osd_info->boxColorOK;
      // 通过首字母来判断绘制颜色 是报警信息还是正确信息
      if (osdd_ifo.texts[idx][0] == 'N') {
        drawWord.enFontColor = (RGN_FONT_COLOR_E)osd_info->boxColorNG;
        box_color = osd_info->boxColorNG;
      }
      drawWord.stPoint.u32X = osdd_ifo.x;
      // 边界检查
      if (osdd_ifo.y - (int(idx)+1)*30 < 0) { error = 1; }
      drawWord.stPoint.u32Y = osdd_ifo.y - (int(idx)+1)*30;
      drawWord.bFlushEn = false;
      std::string text;
      common::Utf8ToGbk(osdd_ifo.texts[idx], text);
      drawWord.pu8Str = (unsigned char *)&text[1]; // (uint8_t *)str;
      // drawWord.pu8Str = (unsigned char *)const_cast<char *>(osdd_ifo.texts[idx].substr(1).c_str()); // (uint8_t *)str;
      drawWord.pAddr = bitmapAttr.pAddr;
      drawWord.stSize = bitmapAttr.stSize;
      if (error == 0) { 
          ret = HB_RGN_DrawWord(chn.enChnId, &drawWord); 
          if (ret != 0) { printf("HB_RGN_DrawWord failed\n"); }
      }
    }

    error = 0;
    // 边界检查
    if (osdd_ifo.x > osd_info->dstWidth || osdd_ifo.y > osd_info->dstHeight) { error=1; }
    if (osdd_ifo.x+osdd_ifo.w >= osd_info->dstWidth) { osdd_ifo.w = osd_info->dstWidth - osdd_ifo.x; }
    if (osdd_ifo.y+osdd_ifo.h >= osd_info->dstHeight) { osdd_ifo.h = osd_info->dstHeight - osdd_ifo.y; }

    std::vector<RGN_POINT_S> points = {{osdd_ifo.x,osdd_ifo.y}, 
                                    {osdd_ifo.x+osdd_ifo.w,osdd_ifo.y}, 
                                    {osdd_ifo.x+osdd_ifo.w,osdd_ifo.y+osdd_ifo.h}, 
                                    {osdd_ifo.x,osdd_ifo.y+osdd_ifo.h}};

    for (int idx = 0; idx < 4; idx++) {
      drawLine[idx].stStartPoint = points[idx];
      drawLine[idx].stEndPoint = points[(idx+1)%4];
      drawLine[idx].pAddr = bitmapAttr.pAddr;
      drawLine[idx].stSize = bitmapAttr.stSize;
      drawLine[idx].u32Thick = osd_info->boxThick;
      drawLine[idx].u32Color = box_color;
      drawLine[idx].bFlushEn = false;
    }
    if (error == 0) { HB_RGN_DrawLineArray(chn.enChnId, drawLine, 4); }

  }


  hb_vio_buffer_t out_buf;
  ret = HB_RGN_SetBitMap(chn.enChnId, &bitmapAttr);
  if (ret != 0) {  printf("HB_RGN_SetBitMap failed\n"); goto exit; }

  std::copy(buffer.yuv.begin(), buffer.yuv.begin()+size_y, feedback_buf.img_addr.addr[0]);
  std::copy(buffer.yuv.begin()+size_y, buffer.yuv.end(), feedback_buf.img_addr.addr[1]);
  // memcpy(feedback_buf.img_addr.addr[1], (void*)buffer.data() + size_y, sizeof(char)*size_uv);
  // read(img_in_fd, feedback_buf.img_addr.addr[0], size_y);
  // read(img_in_fd, feedback_buf.img_addr.addr[1], size_uv);

  ret = HB_VPS_SendFrame(osd_info->VpsGrp, &feedback_buf, 1000);
  if (ret != 0) { goto exit; }
  ret = HB_VPS_GetChnFrame(osd_info->VpsGrp, osd_info->vpsChn, &out_buf, 2000);
  if (ret != 0) { goto exit; }
  // printf("HB_VPS. GetChnFrame: %d\n", ret);
  size_y = out_buf.img_addr.stride_size * out_buf.img_addr.height;
  size_uv = size_y / 2;
  // printf("HB_VPS. out:stride size = %d height = %d\n", out_buf.img_addr.stride_size, out_buf.img_addr.height);
  
  std::copy(out_buf.img_addr.addr[0], out_buf.img_addr.addr[0] + size_y, buffer.yuv.begin());
  std::copy(out_buf.img_addr.addr[1], out_buf.img_addr.addr[1] + size_uv, buffer.yuv.begin()+size_y);
  ret = HB_VPS_ReleaseChnFrame(osd_info->VpsGrp, osd_info->vpsChn, &out_buf);


exit:
  // mtx.unlock();
  return ret;
}

int HBOsdText::set_draw_params() {
  int ret = 0;
  HBStreamOsdInfo* osd_info = (HBStreamOsdInfo *)osd_ifo;

  RGN_ATTR_S pstRegion;
  pstRegion.enType = OVERLAY_RGN;
  pstRegion.stOverlayAttr.stSize.u32Width = osd_info->dstWidth;
  pstRegion.stOverlayAttr.stSize.u32Height = osd_info->dstHeight;
  pstRegion.stOverlayAttr.enPixelFmt = PIXEL_FORMAT_VGA_4;
  pstRegion.stOverlayAttr.enBgColor  = FONT_KEY_COLOR;


  RGN_CHN_ATTR_S chn_attr;
  chn_attr.bShow = true;
  chn_attr.bInvertEn = false;
  // chn_attr.enType = OVERLAY_RGN;
  chn_attr.unChnAttr.stOverlayChn.stPoint.u32X = 0;
  chn_attr.unChnAttr.stOverlayChn.stPoint.u32Y = 0;
  // chn_attr.unChnAttr.stOverlayChn.u32Layer = 3;

  bitmapAttr.enPixelFormat = PIXEL_FORMAT_VGA_4;
  bitmapAttr.stSize = pstRegion.stOverlayAttr.stSize;
  size_t mem_size = osd_info->dstWidth * osd_info->dstHeight / 2;
  bitmapAttr.pAddr = malloc(mem_size);
  memset(bitmapAttr.pAddr, 0xff, mem_size);

  ret = HB_RGN_Create(chn.enChnId, &pstRegion);
  if (ret != 0) { printf("HB_RGN_Create failed\n"); goto gofree; }
  ret = HB_RGN_AttachToChn(chn.enChnId, &chn, &chn_attr);
  if (ret != 0) { printf("HB_RGN_AttachToChn failed\n"); goto gofree; }
  return 0;

gofree:
  free(bitmapAttr.pAddr); 
  return ret;
}

int HBOsdText::release_draw_params() {
  int ret = 0;
  HBStreamOsdInfo* osd_info = (HBStreamOsdInfo *)osd_ifo;
  ret = HB_RGN_DetachFromChn(chn.enChnId, &chn);
  if (ret) { printf("HB_RGN_DetachFromChn failed\n"); }

  ret = HB_RGN_Destroy(chn.enChnId);
  if (ret) { printf("HB_RGN_Destroy failed\n"); }

  if (bitmapAttr.pAddr) free(bitmapAttr.pAddr); 

  HB_VPS_DisableChn(osd_info->VpsGrp, osd_info->vpsChn);

  HB_VPS_StopGrp(osd_info->VpsGrp);
  HB_VPS_DestroyGrp(osd_info->VpsGrp);

  // TODO: 正确释放内存
  // if (feedback_buf.img_addr.addr[0]) free(feedback_buf.img_addr.addr[0]);
  // if (feedback_buf.img_addr.addr[1]) free(feedback_buf.img_addr.addr[1]);
  // hb_vio_free_ipubuf(feedback_buf.img_info.pipeline_id, &feedback_buf);
  // HB_VPS_ReleaseChnFrame(osd_info->VpsGrp, osd_info->vpsChn, &feedback_buf);

  ion_close(feedback_buf.img_info.fd[0]);
  ion_close(feedback_buf.img_info.fd[1]);
  return 0;
}


}